
/**
 * Serial extension for LUA
 * based on the ezV24 library libezV24.so.0.1
 * Jean-pierre MANDON 2007
 * jp dot mandon at free dot fr
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

#include <ezV24/ezV24.h>

#define MYNAME		"rs232"
#define VERSION	        "$Id: rs232.c,v beta 2007/07/07 09:00:00 jpm Exp $"

#include "lua.h"
#include "lauxlib.h"

/**
 * Find and return the version identifier from our CVS marker.
 * @return A static memory buffer containing the version number
 */
char * getVersion( )
{
    char *start  = NULL;
    char *end    = NULL;
    int length   = 0;
    char *prolog = "Release " RELEASE " CVS ID v";

    static char marker[128] = {'\0'};

    /*
     * If we have already calculated the revision information
     * then return it.
     */
    if ( strlen( marker ) > 0 )
    {
	return( marker );
    }


    start = strstr( VERSION, ",v " );
    if ( start == NULL )
	return NULL;

    /* Add on the ",v " text. */
    start += 3;
   
    /* Now find the next space - after the version marker */
    end = strstr( start, " " );
    if ( end == NULL )
	return NULL;


    /* The length of the version marker revision ID. */
    length  = end - start;

    /* Add in the prolog + revision information */
    strncpy( marker, prolog, strlen( prolog ) );
    strncat( marker, start, length );

    return( marker );
}

/**
 * Convert the given octal value to a decimal integer.
 * @param octal The value to convert.
 * @return The decimal representation.
 */
static int oct2decimal( int octal )
{
    char val[20];
    int  decval = 0;
    int i;

    sprintf(val,"%d", octal );

    for( i = 0; i < strlen( val ) ; i ++ )
    {
	int octval = val[i];
	
	decval *= 8;
	octval  = octval - '0';
	
	/* Invalid digit */
	if (octval > 7)
	    return( -1 );
    
	decval += octval;
    }

    return(decval);
}

/* ezV24 routines for LUA */

v24_port_t *UsedPort=NULL;

static void mySignalHandler ( int reason )
{
    /* just close the serial port */
    v24ClosePort(UsedPort);
    exit(99);
}

static void installSignalhandler ( void )
{
    signal(SIGINT,mySignalHandler);
    signal(SIGTERM,mySignalHandler);    
}

/*
V24OpenPort function
format: V24OpenPort("device",option1,option2)
where:  device is the name of the port ( /dev/ttyS0 on my ubuntu )
	option 1
    	0 = V24_STANDARD 	just empty
    	1 = V24_LOCK	 	lock the port
    	2 = V24_NO_DELAY	no wait on DCD while opening the port
	option2
    	0 = V24_RTS_CTS		use hardware handshakes
    	1 = V24_XON_XOFF	use software handshakes
    	2 = V24_DROP_DTR	drop DTR on close the port
    	3 = V24_NON_BLOCK	non blocking read
    	4 = V24_DEBUG_ON	enable stderr messages
result of the fonction is a string. If this string is OK, port is opened 
*/

static int pV24OpenPort(lua_State *L)
{
const char *port;
int option1,option2;
unsigned int Flags=V24_STANDARD;

if (lua_isstring(L, 1))
	port = lua_tostring(L, 1);
else
	{
	lua_pushstring(L,"Port Name");
	return 1;
	}
if (lua_isnumber(L,2))
	option1 = lua_tonumber(L,2);
else
	{
	lua_pushstring(L,"Bad option 1");
	return 1;
	}

switch(option1) {
	case 0: Flags=V24_STANDARD; break;
	case 1: Flags=V24_LOCK; break;
	case 2: Flags=V24_NO_DELAY; break;
		}
	
/* Bad value for option 1 */

if (option1>2) 
	{
	lua_pushstring(L,"Bad value for option 1");
	return 1;
	}

if (lua_isnumber(L,3))
	option2 = lua_tonumber(L,3);
else
	{
	lua_pushstring(L,"Bad option 2");
	return 1;
	}

switch(option2) {
	case 0: Flags=Flags | V24_RTS_CTS; break;
	case 1: Flags=Flags | V24_XON_XOFF; break;
	case 2: Flags=Flags | V24_DROP_DTR; break;
	case 3: Flags=Flags | V24_NON_BLOCK; break;
	case 4: Flags=Flags | V24_DEBUG_ON; break;
		}

/* Bad value for option 2 */

if (option2>4) 
	{
	lua_pushstring(L,"Bad value for option 2");
	return 1;
	}

installSignalhandler();
UsedPort=v24OpenPort(port,Flags);
if ( UsedPort==NULL )
    {
	lua_pushstring(L,"Not opened");
	return 1;
    }
lua_pushstring(L,"OK");
return 1;
}

/*
V24ClosePort function
format: V24ClosePort()
result of the fonction is a string. If this string is OK, port is closed
*/

static int pV24ClosePort(lua_State *L)
{
static int result;
result=v24ClosePort(UsedPort);
if ( result!=V24_E_OK )
    {
	lua_pushstring(L,"Error");
	return 1;
    }
lua_pushstring(L,"OK");
return 1;
}

/*
V24SetParameters function
format: V24SetParameters(speed,datasize,parity)
where:  speed is the baudrate of the opened port
    	1 = V24_B50				50 baud
    	2 = V24_B75				75 baud
    	3 = V24_B110				110 baud
    	4 = V24_B134				134.5 baud
    	5 = V24_B150				150 baud
    	6 = V24_B200				200 baud	
    	7 = V24_B300				300 baud
    	8 = V24_B600				600 baud
    	9 = V24_B1200				1200 baud
    	10 = V24_B1800				1800 baud
    	11 = V24_B2400				2400 baud
    	12 = V24_B4800				4800 baud
    	13 = V24_B9600				9600 baud
    	14 = V24_B19200				19200 baud
    	15 = V24_B38400				38400 baud
    	16 = V24_B57600				57600 baud
    	17 = V24_B115200			115200 baud
	datasize is the size of the frame
    	0 = V24_5BIT
    	1 = V24_6BIT
    	2 = V24_7BIT
	3 = V24_8BIT
	parity
    	0 = V24_NONE				disable parity bit	
    	1 = V24_EVEN				even parity	
    	2 = V24_ODD				odd parity	
    	3 = V24_IGNORE				use parity but do not test it	
result of the fonction is a string. If this string is OK, parameters are fixed
*/

static int pV24SetParameters(lua_State *L)
{
static int baudrate;
static int datasize;
static int parity;
static int result;

if (lua_isnumber(L,1))
	baudrate = lua_tonumber(L,1);
else
	{
	lua_pushstring(L,"Bad format for baud rate");
	return 1; 
	}
if (lua_isnumber(L,2))
	datasize = lua_tonumber(L,2);
else
	{
	lua_pushstring(L,"Bad format for datasize");
	return 1;
	}
if (lua_isnumber(L,3))
	parity = lua_tonumber(L,3);
else
	{
	lua_pushstring(L,"Bad format for parity");
	return 1;
	}
result=v24SetParameters(UsedPort,baudrate,datasize,parity);
if ( result!=V24_E_OK )
    {
	lua_pushstring(L,"Error settings parameters");
	v24ClosePort(UsedPort);
	return 1;
    }
lua_pushstring(L,"OK");
return 1;
}

/*
V24SetStopbits function
format: V24SetStopbits(number of stop bits)
where:  number of stop bits is 1 or 2
result of the fonction is a string. If this string is OK, stop bits are fixed
*/

static int pV24SetStopbits(lua_State *L)
{
static int nbbits;
static int result;

if (lua_isnumber(L,1))
	nbbits = lua_tonumber(L,1);
else
	{
	lua_pushstring(L,"Bad format for number");
	return 1; 
	}
result=v24SetStopbits(UsedPort,nbbits);
if ( result!=V24_E_OK )
    {
	lua_pushstring(L,"Error settings stop bits");
	v24ClosePort(UsedPort);
	return 1;
    }
lua_pushstring(L,"OK");
return 1;
}

/*
V24SetTimeouts function
format: V24SetTimeouts(timeout)
where:  timeout duration is specified in tenth of seconds 
result of the fonction is a string. If this string is OK, timeout is fixed
*/

int pV24SetTimeouts(lua_State *L)
{
static int time;
static int result;

if (lua_isnumber(L,1))
	time = lua_tonumber(L,1);
else
	{
	lua_pushstring(L,"Bad format for timeout");
	return 1; 
	}
result=v24SetTimeouts(UsedPort,time);
if ( result!=V24_E_OK )
    {
	lua_pushstring(L,"Error settings timeout");
	v24ClosePort(UsedPort);
	return 1;
    }
lua_pushstring(L,"OK");
return 1;
}

/*
V24Putc function
format: V24Putc(character)
where:  Character is sent on the serial port 
result of the fonction is a string. If this string is OK, character is sent
*/

static int pV24Putc(lua_State *L)
{
static unsigned char character;
static int result;

if (lua_isnumber(L,1))
	character = lua_tonumber(L,1);
else
	{
	lua_pushstring(L,"Bad format for character");
	return 1; 
	}
result=v24Putc(UsedPort,character);
if ( result!=V24_E_OK )
    {
	lua_pushstring(L,"Error sending character");
	v24ClosePort(UsedPort);
	return 1;
    }
lua_pushstring(L,"OK");
return 1;
}

/*
V24Write function
format: V24Write(string,length_of_string)
where:  string is sent on the serial port 
result of the fonction is a string. If this string is OK, string is sent
*/

static int pV24Write(lua_State *L)
{
const unsigned char *line;
static int result;
int lengthofline;

if (lua_isstring(L, 1)) line = lua_tostring(L, 1);
else	{
	lua_pushstring(L,"Not a string");
	return 1;
	}
if (lua_isnumber(L,2)) lengthofline = lua_tonumber(L,2);
else	{
	lua_pushstring(L,"error format length of line");
	return 1; 
	}
result=v24Write(UsedPort,line,lengthofline);
lua_pushstring(L,"OK");
return 1;
}

/*
V24Getc function
format: V24Getc()
a character is waited on the serial port 
result of the fonction is an integer. If the character is received, result is 1
*/

static int pV24Getc(lua_State *L)
{
static int result;
static int nbcar;

result=-1;
sleep(10);
result=v24Getc(UsedPort);
lua_pushnumber(L,result);
return 1;
}

/*
V24Gets function
format: V24Gets()
a string is waited on the serial port 
result of the fonction is a string.
*/


/**
 * Mappings between the LUA code and our C code.
 */

static const luaL_reg R[] =
{
    {"V24OpenPort",		pV24OpenPort},
    {"V24ClosePort",		pV24ClosePort},
    {"V24SetParameters",	pV24SetParameters},
    {"V24SetStopbits",		pV24SetStopbits},	
    {"V24SetTimeouts",		pV24SetTimeouts},
    {"V24Putc",			pV24Putc},
    {"V24Write",		pV24Write},
    {"V24Getc",			pV24Getc},	
    {NULL,			NULL}
};

/**
 * Bind our exported functions to the Lua intepretter, making our functions
 * available to the calling script.
 * @param L The lua intepreter object.
 * @return 1 on success, 0 on failure.
 */

LUALIB_API int luaopen_rs232 (lua_State *L)
{
    /* Version number from CVS marker. */
    char *version = getVersion();

    luaL_openlib(L, MYNAME, R, 0);
    lua_pushliteral(L,"version");
    lua_pushstring(L, version );
    lua_settable(L,-3);

    return 1;
}

